﻿// ////////////////////////////////////////////////////////////////////////////
// 
// SWE Case Tool
// 
// Solution:	CaseTool
// Project:		3. MethodPercentage
// Filename:	PercentageMethod.cs
// 
// File Created:	10:37 16.05.2013 
// Last Changed:	18:11 05.06.2013
// 
// ////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace SWE.CaseTool.Methods
{
	/// <summary>
	/// The percentage method. the mehtod does not use any project specific information. It only uses the
	/// estimation of 4 predefined phases, on which it will calculate the estimated time.
	/// </summary>
	[MethodName("Prozentsatz Methode"),
	 MethodDescription("Berechnet den Aufwand eines Projektes anhand einer selbst bewerteten Projektphase " +
	                   "oder anhand eine bereits vorhandenen Referenzprojektes.")]
	public class PercentageMethod : IMethod
	{
		/// <summary>
		/// The uniqe method name.
		/// </summary>
		private const String NAME = "Prozentsatz Methode";

		/// <summary>
		/// A referecne to the project global data dictionary.
		/// </summary>
		private readonly Dictionary<string, string> _data;

		/// <summary>
		/// Initializes a new instance of the <see cref="PercentageMethod" /> class.
		/// </summary>
		public PercentageMethod() { Phase = PercentagePhase.Concept; }

		/// <summary>
		/// Initializes a new instance of the <see cref="PercentageMethod" /> class with the data saved in the
		/// project global dictionary.
		/// </summary>
		/// <param name="data">The data.</param>
		/// <exception cref="System.ArgumentException">Unable to deserialize the percentage method</exception>
		public PercentageMethod(Dictionary<string, string> data)
		{
			_data = data;

			string serializedData;
			if (data.TryGetValue(NAME, out serializedData))
			{
				if (!String.IsNullOrWhiteSpace(serializedData))
				{
					try
					{
						var tmp =
							(PercentageMethod) new XmlSerializer(typeof (PercentageMethod)).Deserialize(new StringReader(serializedData));
						tmp.CopyTo(this);
					}
					catch (Exception ex)
					{
						throw new ArgumentException("Unable to deserialize the percentage method", ex);
					}
				}
			}
		}

		/// <summary>
		/// Copies this instance to the given one. Makes a hard copy resulting in two independant but equal
		/// instances.
		/// </summary>
		/// <param name="percentageMethod">The percentage method.</param>
		private void CopyTo(PercentageMethod percentageMethod)
		{
			percentageMethod.UseReferenceProject = UseReferenceProject;
			percentageMethod.Phase = Phase;
			percentageMethod.DurationReferencePhase = DurationReferencePhase;

			percentageMethod.PartRefConcept = PartRefConcept;
			percentageMethod.PartRefCoding = PartRefCoding;
			percentageMethod.PartRefTesting = PartRefTesting;
			percentageMethod.PartRefDefinition = PartRefDefinition;

			percentageMethod.PartConcept = PartConcept;
			percentageMethod.PartCoding = PartCoding;
			percentageMethod.PartTesting = PartTesting;
			percentageMethod.PartDefinition = PartDefinition;
		}

		/// <summary>
		/// Calculates this estimated development time of the current project.
		/// </summary>
		/// <returns>the development time</returns>
		public double Calculate() { return UseReferenceProject ? CalculateReference() : CalculateNew(); }

		/// <summary>
		/// Calculates this estimated development time of the current project. without using a reference
		/// project.
		/// </summary>
		/// <returns>the development time</returns>
		/// <exception cref="System.ArgumentException">
		/// PartConcept must be a percentage value
		/// or
		/// PartConcept must be a percentage value
		/// or
		/// PartConcept must be a percentage value
		/// or
		/// PartConcept must be a percentage value
		/// or
		/// Uknwon phase:  + Phase.ToString()
		/// </exception>
		private double CalculateNew()
		{
			var duration = DurationReferencePhase;
			switch (Phase)
			{
				case PercentagePhase.Concept:
					if (PartConcept <= 0 || PartConcept > 1)
					{
						throw new ArgumentException("PartConcept must be a percentage value");
					}
					duration *= (1/PartConcept);

					break;
				case PercentagePhase.Definition:
					if (PartDefinition <= 0 || PartDefinition > 1)
					{
						throw new ArgumentException("PartConcept must be a percentage value");
					}
					duration *= (1/PartDefinition);

					break;
				case PercentagePhase.Coding:
					if (PartCoding <= 0 || PartCoding > 1)
					{
						throw new ArgumentException("PartConcept must be a percentage value");
					}
					duration *= (1/PartCoding);

					break;
				case PercentagePhase.Testing:
					if (PartTesting <= 0 || PartTesting > 1)
					{
						throw new ArgumentException("PartConcept must be a percentage value");
					}
					duration *= (1/PartTesting);

					break;
				default:
					throw new ArgumentException("Uknwon phase: " + Phase.ToString());
			}

			return duration;
		}


		/// <summary>
		/// Calculates this estimated development time of the current project, with using a reference project.
		/// </summary>
		/// <returns>the development time</returns>
		/// <returns></returns>
		/// <exception cref="System.ArgumentException">
		/// Uknown phase:  + Phase.ToString()
		/// or
		/// Percentage must be a regular percentage value
		/// </exception>
		private double CalculateReference()
		{
			double percentage;
			switch (Phase)
			{
				case PercentagePhase.Definition:
					percentage = (PartDefinition > 0 ? PartDefinition : PartRefDefinition);
					break;
				case PercentagePhase.Concept:
					percentage = (PartConcept > 0 ? PartConcept : PartRefConcept);
					break;
				case PercentagePhase.Coding:
					percentage = (PartCoding > 0 ? PartCoding : PartRefCoding);
					break;
				case PercentagePhase.Testing:
					percentage = (PartTesting > 0 ? PartTesting : PartRefTesting);
					break;

				default:
					throw new ArgumentException("Uknown phase: " + Phase.ToString());
			}

			if (percentage <= 0 || percentage > 1)
			{
				throw new ArgumentException("Percentage must be a regular percentage value");
			}

			return DurationReferencePhase*(1/percentage);
		}

		/// <summary>
		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged
		/// resources and saves the data to the project global dictionary.
		/// </summary>
		public void Dispose()
		{
			var writer = new StringWriter();
			new XmlSerializer(typeof (PercentageMethod)).Serialize(writer, this);
			_data[NAME] = writer.ToString();
		}

		/// <summary>
		/// Gets or sets a value indicating whether [use reference project] or calculate on a new data.
		/// </summary>
		/// <value>
		///     <c>true</c> if [use reference project]; otherwise, <c>false</c>.
		/// </value>
		public bool UseReferenceProject { set; get; }

		/// <summary>
		/// Gets or sets the phase on which the calculation is based on.
		/// </summary>
		/// <value>
		/// The phase.
		/// </value>
		public PercentagePhase Phase { set; get; }

		/// <summary>
		/// Gets or sets the duration reference phase.
		/// </summary>
		/// <value>
		/// The duration reference phase.
		/// </value>
		public double DurationReferencePhase { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the reference phase: concept.
		/// </summary>
		/// <value>
		/// The part ref concept.
		/// </value>
		public double PartRefConcept { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the reference phase: coding.
		/// </summary>
		/// <value>
		/// The part ref coding.
		/// </value>
		public double PartRefCoding { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the reference phase: testing.
		/// </summary>
		/// <value>
		/// The part ref testing.
		/// </value>
		public double PartRefTesting { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the reference phase: definition.
		/// </summary>
		/// <value>
		/// The part ref definition.
		/// </value>
		public double PartRefDefinition { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the phase: concept.
		/// </summary>
		/// <value>
		/// The part concept.
		/// </value>
		public double PartConcept { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the phase: coding.
		/// </summary>
		/// <value>
		/// The part coding.
		/// </value>
		public double PartCoding { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the phase: testing.
		/// </summary>
		/// <value>
		/// The part testing.
		/// </value>
		public double PartTesting { set; get; }

		/// <summary>
		/// Gets or sets the percentual part of the phase: definition.
		/// </summary>
		/// <value>
		/// The part definition.
		/// </value>
		public double PartDefinition { set; get; }
	}
}